#include <Windows.h>
#include "GameEventManager.h"
#pragma comment(lib, "WinMM.Lib")

GameEventManager::GameEventManager(void)
	: miActiveQueue(0)
{

}

GameEventManager::~GameEventManager(void)
{
	mmRegistry = EventListenerMap();
}

bool GameEventManager::addListener(const EventListenerPtr &listener, const EventType type)
{
	// Find listener map for this type
	EventListenerMap::iterator mapIt = mmRegistry.find(type);

	// If there is no listener map entry, create one
	if (mapIt == mmRegistry.end())
		mmRegistry.insert(EventListenerMapEntry(type, EventListenerTable()));

	// Check, if listener is already in the table
	mapIt = mmRegistry.find(type);
	for (EventListenerTable::iterator tableIt = (*mapIt).second.begin(),
		 tableItEnd = (*mapIt).second.end(); tableIt != tableItEnd; ++tableIt)
	{
		if (*tableIt == listener) return false;
	}

	// Add  listener
	(*mapIt).second.push_back(listener);

	return true;
}

bool GameEventManager::removeListener(const EventListenerPtr &listener, const EventType type)
{
	// Find listener map for this type
	EventListenerMap::iterator mapIt = mmRegistry.find(type);

	// Check if the requested type exists
	if (mapIt == mmRegistry.end()) return false;

	// Look for the listener in the list and remove it
	for (EventListenerTable::iterator tableIt = (*mapIt).second.begin(),
			tableItEnd = (*mapIt).second.end(); tableIt != tableItEnd; ++tableIt)
	{
		if (*tableIt == listener)
		{
			(*mapIt).second.erase(tableIt);
			return true;
		}
	}

	return false;
}

bool GameEventManager::queueEvent(const EventPtr &event)
{
	// Check if a someone listens to this event
	if (mmRegistry.find(event->getEventType()) == mmRegistry.end()) return false;

	// Add event to the queue
	maQueues[miActiveQueue].push_back(event);

	return true;
}

bool GameEventManager::process(const unsigned long maxMs)
{
	// Calculate time to pause the event manger
	unsigned long curMs = timeGetTime();
	unsigned long endMs = curMs + maxMs;

	// Switch queues
	int	processQueue = miActiveQueue;

	//miActiveQueue = (miActiveQueue + 1) % 2;
	//maQueues[miActiveQueue].clear();

	while (maQueues[processQueue].size() > 0)
	{
		// Get event to process
		EventPtr event = maQueues[processQueue].front();
		maQueues[processQueue].pop_front();

		EventType type = event->getEventType();

		// Check if someone is listining for this event
		EventListenerMap::const_iterator mapIt = mmRegistry.find(type);
		if (mapIt == mmRegistry.end()) continue;

		const EventListenerTable &table = mapIt->second;

		// Process event for all listeners
		for (EventListenerTable::const_iterator tableIt = table.begin(),
			 tableEndIt = table.end(); tableIt != tableEndIt; ++tableIt)
		{
			(*tableIt)->handleEvent(*event);
		}

		// Check if process time has ended
		curMs = timeGetTime();
		if (curMs >= endMs) break;
	}

	bool queueFlushed = (maQueues[processQueue].size() == 0);

	/*
	if (!queueFlushed)
	{
		// Move remaining events to the active queue
		while (maQueues[processQueue].size() > 0)
		{
			EventPtr event = maQueues[processQueue].back();
			maQueues[processQueue].pop_back();
			maQueues[miActiveQueue].push_front(event);
		}
	}
	*/

	return queueFlushed;
}

void GameEventManager::emptyQueues(void)
{
	maQueues[0].clear();
	maQueues[1].clear();
}